/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "geofence_client.h"

#include <iservice_registry.h>
#include <if_system_ability_manager.h>
#include <ipc_skeleton.h>
#include <system_ability_definition.h>

namespace OHOS {
namespace Msdp {
GeofenceClient::GeofenceClient() {}
GeofenceClient::~GeofenceClient()
{
    if (geofenceProxy_ != nullptr) {
        auto remoteObject = geofenceProxy_->AsObject();
        if (remoteObject != nullptr) {
            remoteObject->RemoveDeathRecipient(deathRecipient_);
        }
    }
}

ErrCode GeofenceClient::Connect()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (geofenceProxy_ != nullptr) {
        return ERR_OK;
    }

    sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (sam == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "GetSystemAbilityManager failed");
        return E_GEOFENCE_GET_SYSTEM_ABILITY_MANAGER_FAILED;
    }

    sptr<IRemoteObject> remoteObject_ = sam->CheckSystemAbility(MSDP_GEOFENCE_SERVICE_ID);
    if (remoteObject_ == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "CheckSystemAbility failed");
        return E_GEOFENCE_GET_SERVICE_FAILED;
    }

    deathRecipient_ = sptr<IRemoteObject::DeathRecipient>(new GeofenceDeathRecipient());
    if (deathRecipient_ == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "Failed to create GeofenceDeathRecipient");
        return ERR_NO_MEMORY;
    }

    if ((remoteObject_->IsProxyObject()) && (!remoteObject_->AddDeathRecipient(deathRecipient_))) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "Add death recipient to Geofence service failed");
        return E_GEOFENCE_ADD_DEATH_RECIPIENT_FAILED;
    }

    geofenceProxy_ = iface_cast<Igeofence>(remoteObject_);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Connecting GeofenceService success");
    return ERR_OK;

}

void GeofenceClient::ResetProxy(const wptr<IRemoteObject>& remote)
{
    std::lock_guard<std::mutex> lock(mutex_);
    GEOFENCE_RETURN_IF(geofenceProxy_ == nullptr);

    auto serviceRemote = geofenceProxy_->AsObject();
    if ((serviceRemote != nullptr) && (serviceRemote == remote.promote())) {
        serviceRemote->RemoveDeathRecipient(deathRecipient_);
        geofenceProxy_ = nullptr;
    }
}

void GeofenceClient::GeofenceDeathRecipient::OnRemoteDied(const wptr<IRemoteObject>& remote)
{
    if (remote == nullptr) {
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "OnRemoteDied failed, remote is nullptr");
        return;
    }

    GeofenceClient::GetInstance().ResetProxy(remote);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Recv death notice");
}

void GeofenceClient::SubscribePoint(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PointOptions& pointOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Enter");
    GEOFENCE_RETURN_IF((callback == nullptr) || (Connect() != ERR_OK));
    if (std::size(pointOptions.centerCoordinate) != 2) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_INNERKIT, "std::size(pointOptions.centerCoordinate) = %{public}d", std::size(pointOptions.centerCoordinate));
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "pointOptions.centerCoordinate error");
        return;
    }
    geofenceProxy_->SubscribePoint(type, pointOptions, callback);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Exit");
}

void GeofenceClient::UnSubscribePoint(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PointOptions& pointOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Enter");
    GEOFENCE_RETURN_IF((callback == nullptr) || (Connect() != ERR_OK));
    if (std::size(pointOptions.centerCoordinate) != 2) {
        GEOFENCE_HILOGI(GEOFENCE_MODULE_INNERKIT, "std::size(pointOptions.centerCoordinate) = %{public}d", std::size(pointOptions.centerCoordinate));
        GEOFENCE_HILOGE(GEOFENCE_MODULE_INNERKIT, "pointOptions.centerCoordinate error");
        return;
    }
    geofenceProxy_->UnSubscribePoint(type, pointOptions, callback);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Exit");
}

void GeofenceClient::SubscribePoi(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PoiOptions& poiOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Enter");
    GEOFENCE_RETURN_IF((callback == nullptr) || (Connect() != ERR_OK));
    geofenceProxy_->SubscribePoi(type, poiOptions, callback);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Exit");
}

void GeofenceClient::UnSubscribePoi(const GeofenceDataUtils::GeofenceType& type,
    const GeofenceDataUtils::PoiOptions& poiOptions, const sptr<IgeofenceCallback>& callback)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Enter");
    GEOFENCE_RETURN_IF((callback == nullptr) || (Connect() != ERR_OK));
    geofenceProxy_->UnSubscribePoi(type, poiOptions, callback);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Exit");
}

bool GeofenceClient::SetFoolproofTactics(const GeofenceDataUtils::GeofenceType& type, double minuteTime)
{
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Enter");
    return geofenceProxy_->SetFoolproofTactics(type, minuteTime);
    GEOFENCE_HILOGD(GEOFENCE_MODULE_INNERKIT, "Exit");
}

} // namespace Msdp
} // namespace OHOS
